{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "fbb1824a-cd01-4258-8add-d0773ae69fcc",
   "metadata": {},
   "source": [
    "## Neo4j GraphDatabase\n",
    "\n",
    "https://neo4j.com/docs/python-manual/current/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "13391506-63db-4b32-b896-d0e91406afd4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name: neo4j\n",
      "Version: 5.19.0\n",
      "Summary: Neo4j Bolt driver for Python\n",
      "Home-page: \n",
      "Author: \n",
      "Author-email: \"Neo4j, Inc.\" <drivers@neo4j.com>\n",
      "License: Apache License, Version 2.0\n",
      "Location: /opt/conda/lib/python3.11/site-packages\n",
      "Requires: pytz\n",
      "Required-by: \n"
     ]
    }
   ],
   "source": [
    "!pip show neo4j"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30061e92-3a5a-4bac-b749-61531ee9a7db",
   "metadata": {},
   "source": [
    "## Connect to database\n",
    "\n",
    "Please note the 2nd `neo4j` in URI \"neo4j://neo4j:7687\" is the neo4j service name defined in the compose-neo4j.yml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "10fd5b0d-2726-4a31-849f-9efcb57c6bab",
   "metadata": {},
   "outputs": [],
   "source": [
    "from neo4j import GraphDatabase\n",
    "\n",
    "URI = \"neo4j://neo4j:7687\"\n",
    "AUTH = (\"neo4j\", \"neo4j123\") \n",
    "\n",
    "driver = GraphDatabase.driver(URI, auth=AUTH) \n",
    "    \n",
    "driver.verify_connectivity()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2cbeaa7-4b15-41d0-b9dd-38492e7b6225",
   "metadata": {},
   "source": [
    "## Create Node\n",
    "\n",
    "Create a node called \"Person\" if it does not exist, otherwise merging to the existing node. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b8b8c281-1477-409c-8838-4b353b8beef4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Created 1 nodes in 238 ms.\n"
     ]
    }
   ],
   "source": [
    "summary = driver.execute_query(\n",
    "    \"MERGE (:Person {name: $name})\",  \n",
    "    name=\"Alice\",  \n",
    "    database_=\"neo4j\",  \n",
    ").summary\n",
    "\n",
    "print(\"Created {nodes_created} nodes in {time} ms.\".format(\n",
    "    nodes_created=summary.counters.nodes_created,\n",
    "    time=summary.result_available_after\n",
    "))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07446639-12e2-468e-9e43-b3acaa30b992",
   "metadata": {},
   "source": [
    "## Update Node"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "c2db7b15-7212-4aa0-b9f8-1f13f706bf05",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Query counters: {'_contains_updates': True, 'properties_set': 1}.\n"
     ]
    }
   ],
   "source": [
    "records, summary, keys = driver.execute_query(\"\"\"\n",
    "    MATCH (p:Person {name: $name})\n",
    "    SET p.age = $age\n",
    "    \"\"\", name=\"Alice\", age=42,\n",
    "    database_=\"neo4j\",\n",
    ")\n",
    "\n",
    "print(f\"Query counters: {summary.counters}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77aadc44-5bc8-41ca-b936-baf1e4e8a310",
   "metadata": {},
   "source": [
    "## Query Node\n",
    "\n",
    "https://neo4j.com/docs/python-manual/current/transformers/#_result_as_a_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "f30395ec-f0b2-4941-ae56-b4c42ba68e64",
   "metadata": {},
   "outputs": [],
   "source": [
    "records, summary, keys = driver.execute_query(\n",
    "    \"MATCH (p:Person) where p.name='Alice' RETURN p.name AS name, p.age as age\",\n",
    "    database_=\"neo4j\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "7ed055d3-f535-4308-9b4e-4231215152ab",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'name': 'Alice', 'age': 42}\n"
     ]
    }
   ],
   "source": [
    "# Loop through results and do something with them\n",
    "for record in records:  \n",
    "    print(record.data())  # obtain record as dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "c4c446c4-a090-41c0-8bf9-014a38509e7b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The query `MATCH (p:Person) where p.name='Alice' RETURN p.name AS name, p.age as age` returned 1 records in 84 ms.\n"
     ]
    }
   ],
   "source": [
    "# Summary information  \n",
    "print(\"The query `{query}` returned {records_count} records in {time} ms.\".format(\n",
    "    query=summary.query, \n",
    "    records_count=len(records),\n",
    "    time=summary.result_available_after\n",
    "))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f2ba090-cd1c-46ce-8a5b-0f932c006b53",
   "metadata": {},
   "source": [
    "## Match & Merge\n",
    "\n",
    "`MATCH` - Lookup an exising node \"Alice\" \n",
    "\n",
    "`MERGE` - Create a new node \"Bob\" with relationship \"KNOWS\" to the existing node \"Alice\" "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "09c030f3-8136-4984-bc15-ce8b73cc087c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Query counters: {'_contains_updates': True, 'labels_added': 1, 'relationships_created': 1, 'nodes_created': 1, 'properties_set': 1}.\n"
     ]
    }
   ],
   "source": [
    "records, summary, keys = driver.execute_query(\"\"\"\n",
    "    MATCH (p:Person {name: $name})\n",
    "    MERGE (p)-[:KNOWS]->(:Person {name: $friend})\n",
    "    \"\"\", name=\"Alice\", friend=\"Bob\",\n",
    "    database_=\"neo4j\",\n",
    ")\n",
    "\n",
    "print(f\"Query counters: {summary.counters}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07008e9b-f628-4093-8726-9ee229c2da2c",
   "metadata": {},
   "source": [
    "## Delete Node"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f0166ff8-93a6-49dc-a9d5-a0855228c9c4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Query counters: {'_contains_updates': True, 'nodes_deleted': 1, 'relationships_deleted': 1}.\n"
     ]
    }
   ],
   "source": [
    "records, summary, keys = driver.execute_query(\"\"\"\n",
    "    MATCH (p:Person {name: $name})\n",
    "    DETACH DELETE p\n",
    "    \"\"\", name=\"Alice\",\n",
    "    database_=\"neo4j\",\n",
    ")\n",
    "\n",
    "print(f\"Query counters: {summary.counters}.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "438e1832-0948-475c-ad09-7cfba640a54a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'name': 'Personal Banking Customer', 'age': None}\n",
      "{'name': 'Customer Service Staff', 'age': None}\n",
      "{'name': 'Back Office Staff', 'age': None}\n",
      "{'name': 'Bob', 'age': None}\n"
     ]
    }
   ],
   "source": [
    "records, summary, keys = driver.execute_query(\n",
    "    \"MATCH (p:Person) RETURN p.name AS name, p.age as age\",\n",
    "    database_=\"neo4j\",\n",
    ")\n",
    "\n",
    "# Loop through results and do something with them\n",
    "for record in records:  \n",
    "    print(record.data())  # obtain record as dict"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b217714c-c22c-4e8e-8171-9a1b6372e23f",
   "metadata": {},
   "source": [
    "## Create Relationship\n",
    "\n",
    "- Person (Bob) Uses System (MWS)\n",
    "- Person (Bob) Uses System (iQA)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "ff102f97-fb2f-4d35-974f-e92e525eac6b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Query counters: {'_contains_updates': True, 'labels_added': 1, 'relationships_created': 1, 'nodes_created': 1, 'properties_set': 1}.\n"
     ]
    }
   ],
   "source": [
    "records, summary, keys = driver.execute_query(\"\"\"\n",
    "    MATCH (p:Person {name: $name})\n",
    "    MERGE (p)-[:Uses]->(:System {name: $system})\n",
    "    \"\"\", name=\"Bob\", system=\"MWS\",\n",
    "    database_=\"neo4j\",\n",
    ")\n",
    "\n",
    "print(f\"Query counters: {summary.counters}.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "be207c4a-a444-48d9-aa07-99e2253fcbc6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Query counters: {'_contains_updates': True, 'labels_added': 1, 'relationships_created': 1, 'nodes_created': 1, 'properties_set': 1}.\n"
     ]
    }
   ],
   "source": [
    "records, summary, keys = driver.execute_query(\"\"\"\n",
    "    MATCH (p:Person {name: $name})\n",
    "    MERGE (p)-[:Uses]->(:System {name: $system})\n",
    "    \"\"\", name=\"Bob\", system=\"IQA\",\n",
    "    database_=\"neo4j\",\n",
    ")\n",
    "\n",
    "print(f\"Query counters: {summary.counters}.\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b0e744f-b178-4830-ae87-61db33ac4467",
   "metadata": {},
   "source": [
    "## Transform to graph\n",
    "\n",
    "https://neo4j.com/docs/python-manual/current/transformers/#_transform_to_graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "4b243339-1235-42d2-9b57-be4e5a681ae2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.mirrors.ustc.edu.cn/simple/\n",
      "Collecting pyvis\n",
      "  Downloading https://mirrors.ustc.edu.cn/pypi/packages/ab/4b/e37e4e5d5ee1179694917b445768bdbfb084f5a59ecd38089d3413d4c70f/pyvis-0.3.2-py3-none-any.whl (756 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m756.0/756.0 kB\u001b[0m \u001b[31m4.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n",
      "\u001b[?25hRequirement already satisfied: ipython>=5.3.0 in /opt/conda/lib/python3.11/site-packages (from pyvis) (8.16.1)\n",
      "Requirement already satisfied: jinja2>=2.9.6 in /opt/conda/lib/python3.11/site-packages (from pyvis) (3.1.2)\n",
      "Collecting jsonpickle>=1.4.1 (from pyvis)\n",
      "  Downloading https://mirrors.ustc.edu.cn/pypi/packages/a1/64/815460f86d94c9e1431800a75061719824c6fef14d88a6117eba3126cd5b/jsonpickle-4.0.0-py3-none-any.whl (46 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m46.2/46.2 kB\u001b[0m \u001b[31m15.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hRequirement already satisfied: networkx>=1.11 in /opt/conda/lib/python3.11/site-packages (from pyvis) (3.2)\n",
      "Requirement already satisfied: backcall in /opt/conda/lib/python3.11/site-packages (from ipython>=5.3.0->pyvis) (0.2.0)\n",
      "Requirement already satisfied: decorator in /opt/conda/lib/python3.11/site-packages (from ipython>=5.3.0->pyvis) (5.1.1)\n",
      "Requirement already satisfied: jedi>=0.16 in /opt/conda/lib/python3.11/site-packages (from ipython>=5.3.0->pyvis) (0.19.1)\n",
      "Requirement already satisfied: matplotlib-inline in /opt/conda/lib/python3.11/site-packages (from ipython>=5.3.0->pyvis) (0.1.6)\n",
      "Requirement already satisfied: pickleshare in /opt/conda/lib/python3.11/site-packages (from ipython>=5.3.0->pyvis) (0.7.5)\n",
      "Requirement already satisfied: prompt-toolkit!=3.0.37,<3.1.0,>=3.0.30 in /opt/conda/lib/python3.11/site-packages (from ipython>=5.3.0->pyvis) (3.0.39)\n",
      "Requirement already satisfied: pygments>=2.4.0 in /opt/conda/lib/python3.11/site-packages (from ipython>=5.3.0->pyvis) (2.16.1)\n",
      "Requirement already satisfied: stack-data in /opt/conda/lib/python3.11/site-packages (from ipython>=5.3.0->pyvis) (0.6.2)\n",
      "Requirement already satisfied: traitlets>=5 in /opt/conda/lib/python3.11/site-packages (from ipython>=5.3.0->pyvis) (5.11.2)\n",
      "Requirement already satisfied: pexpect>4.3 in /opt/conda/lib/python3.11/site-packages (from ipython>=5.3.0->pyvis) (4.8.0)\n",
      "Requirement already satisfied: MarkupSafe>=2.0 in /opt/conda/lib/python3.11/site-packages (from jinja2>=2.9.6->pyvis) (2.1.3)\n",
      "Requirement already satisfied: parso<0.9.0,>=0.8.3 in /opt/conda/lib/python3.11/site-packages (from jedi>=0.16->ipython>=5.3.0->pyvis) (0.8.3)\n",
      "Requirement already satisfied: ptyprocess>=0.5 in /opt/conda/lib/python3.11/site-packages (from pexpect>4.3->ipython>=5.3.0->pyvis) (0.7.0)\n",
      "Requirement already satisfied: wcwidth in /opt/conda/lib/python3.11/site-packages (from prompt-toolkit!=3.0.37,<3.1.0,>=3.0.30->ipython>=5.3.0->pyvis) (0.2.8)\n",
      "Requirement already satisfied: executing>=1.2.0 in /opt/conda/lib/python3.11/site-packages (from stack-data->ipython>=5.3.0->pyvis) (1.2.0)\n",
      "Requirement already satisfied: asttokens>=2.1.0 in /opt/conda/lib/python3.11/site-packages (from stack-data->ipython>=5.3.0->pyvis) (2.4.0)\n",
      "Requirement already satisfied: pure-eval in /opt/conda/lib/python3.11/site-packages (from stack-data->ipython>=5.3.0->pyvis) (0.2.2)\n",
      "Requirement already satisfied: six>=1.12.0 in /opt/conda/lib/python3.11/site-packages (from asttokens>=2.1.0->stack-data->ipython>=5.3.0->pyvis) (1.16.0)\n",
      "Installing collected packages: jsonpickle, pyvis\n",
      "Successfully installed jsonpickle-4.0.0 pyvis-0.3.2\n"
     ]
    }
   ],
   "source": [
    "!pip install pyvis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "856202c1-ad1c-4619-8ebe-c6849a6cf8a7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name: pyvis\n",
      "Version: 0.3.2\n",
      "Summary: A Python network graph visualization library\n",
      "Home-page: https://github.com/WestHealth/pyvis\n",
      "Author: Jose Unpingco\n",
      "Author-email: pypi@westhealth.org\n",
      "License: BSD\n",
      "Location: /opt/conda/lib/python3.11/site-packages\n",
      "Requires: ipython, jinja2, jsonpickle, networkx\n",
      "Required-by: \n"
     ]
    }
   ],
   "source": [
    "!pip show pyvis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "fa78d535-b1fc-4af7-9acb-869d73665e90",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pyvis\n",
    "import neo4j\n",
    "\n",
    "# Query to get a graphy result\n",
    "graph_result = driver.execute_query(\"\"\"\n",
    "    MATCH (a:Person {name: $name})-[r]-(b)\n",
    "    RETURN a, r, b\n",
    "    \"\"\", name=\"Bob\",\n",
    "    result_transformer_=neo4j.Result.graph,\n",
    ")\n",
    "\n",
    "# Draw graph\n",
    "nodes_text_properties = {  # what property to use as text for each node\n",
    "    \"Person\": \"name\",\n",
    "    \"System\": \"name\",\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "20b532c0-f54f-4131-b8c9-f16d8459c8c8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "neo4j_graph.html\n"
     ]
    }
   ],
   "source": [
    "visual_graph = pyvis.network.Network()\n",
    "\n",
    "for node in graph_result.nodes:\n",
    "    node_label = list(node.labels)[0]\n",
    "    node_text = node[nodes_text_properties[node_label]]\n",
    "    visual_graph.add_node(node.element_id, node_text, group=node_label)\n",
    "\n",
    "for relationship in graph_result.relationships:\n",
    "    visual_graph.add_edge(\n",
    "        relationship.start_node.element_id,\n",
    "        relationship.end_node.element_id,\n",
    "        title=relationship.type\n",
    "    )\n",
    "\n",
    "visual_graph.show('neo4j_graph.html', notebook=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "057e9e01-17a1-4072-9be6-58440da858ed",
   "metadata": {},
   "source": [
    "Open the `neo4j_graph.html` in browser to view the graph."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99bf4ff3-193a-4ee5-b888-76cac98ffdb7",
   "metadata": {},
   "source": [
    "## Close connection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "c782fe61-6a63-49f9-b304-facf1c7173c0",
   "metadata": {},
   "outputs": [],
   "source": [
    "driver.close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
